package cn.autumnorange.app.common.consumer.rpc.lock.strategy;

import cn.autumnorange.app.common.consumer.rpc.ConsumerRPCService;
import cn.autumnorange.app.common.consumerproducer.util.ClassUtil;
import cn.autumnorange.app.common.dto.ResultDto;
import cn.autumnorange.app.common.rpc.anntation.AbstractAnnotationStrategy;
import cn.autumnorange.app.common.rpc.lock.RPCIdempotentLockTarget;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RFuture;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.http.ResponseEntity;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/** 前端传入rpcid header头作为锁,没有则校验控制层注解是否有锁注解并取对应对象id,对于聚合对象循环遍历处理找到对应指定的类名或者表名+id对象 如xxx.xxx.User#id */
@Slf4j
public class RpcidAnnotationnStrategy extends AbstractAnnotationStrategy {
  public static String ANNOTATIONSTRATEGY = RPCIdempotentLockTarget.class.getName();
  private ConsumerRPCService consumerRPCService;

  public RpcidAnnotationnStrategy() {};

  public RpcidAnnotationnStrategy(
      RedissonClient redissonClient,
      ConsumerRPCService consumerRPCService,
      long connectTimeout,
      long readTimeout,
      int maxAutoRetries,
      int maxAutoRetriesNextServer) {
    this.redissonClient = redissonClient;
    this.connectTimeout = connectTimeout;
    this.readTimeout = readTimeout;
    this.consumerRPCService = consumerRPCService;
    this.maxAutoRetries = maxAutoRetries;
    this.maxAutoRetriesNextServer = maxAutoRetriesNextServer;
  }

  private RedissonClient redissonClient;

  private long connectTimeout;

  private long readTimeout;

  private int maxAutoRetries;

  private int maxAutoRetriesNextServer;
  //  //   重试负载均衡其他节点的最大重试次数，不包括首次server
  //  private int maxAutoRetries;
  //  //     负载到其他服务器的请求总次数
  //  private int maxAutoRetriesNextServer;

  @Override
  public Object annotationTargetHandle(MethodInvocation invocation, Annotation annotation)
      throws Throwable {
    Object result = null;
    String rpcId = consumerRPCService.getRPCId();
    RPCIdempotentLockTarget rpcIdempotentLockTarget=null;

    // 锁时间为消费端调用生产端的总时间
    //    String lockTime=consumerRPCService.getRPCLockTimeToHeader();
    // 前端未分配锁id 处理后端是否注解锁id
//    String isLockThreadEnd = null;
    Exception exception=null;
    if (StringUtils.isBlank(rpcId)) {
      rpcIdempotentLockTarget = (RPCIdempotentLockTarget) annotation;

      if (rpcIdempotentLockTarget != null) {
        String rpcLockAnnotation = rpcIdempotentLockTarget.rpcLock();

        String[] feildKeys = rpcLockAnnotation.split("#");
//        if (classAndLockKeyName.length == 3) {
//          isLockThreadEnd = classAndLockKeyName[2];
//        }
        Object[] arguments = invocation.getArguments();
        if (arguments != null && arguments.length > 0) {
            LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer =
                    new LocalVariableTableParameterNameDiscoverer();
            String[] parameterNames =
                    localVariableTableParameterNameDiscoverer.getParameterNames(invocation.getMethod());
            rpcId =
              (String)
                  ClassUtil.getTargetFeildByFeildKey(
                          feildKeys, arguments, parameterNames);
            if(StringUtils.isBlank(rpcId)){
              throw new RuntimeException("注解rpcId锁 "+feildKeys.toString()+" null,请检查相关注解对应入参字段值是否存在");
            }
        }
      }
    }
    //    Object[] args = point.getArgs(); // 获取目标方法的所有参数的值
    if (StringUtils.isNotBlank(rpcId)) {
      // 锁id通过请求头传到生产端
      if (StringUtils.isBlank(consumerRPCService.getRPCId())) {
        consumerRPCService.setRPCId(rpcId);
      }
      int rpcTotal = maxAutoRetries + 1 + maxAutoRetriesNextServer * (maxAutoRetries + 1);

      long rpcTimeOut =
          (connectTimeout + readTimeout + ConsumerRPCService.MORERPCTIMEOUT) * rpcTotal;
      // 加锁是否成功
      boolean isLock = false;
      RLock lock = null;
      try {
        lock = redissonClient.getLock(rpcId);
        // 可以做一些前置处理
        //      对该次请求流程加锁实现幂等
        //      redisLockRegistry.obtain(ricId).tryLock();
        //      加锁结果
        //      log.info("切面介入工作....前置通知 获得header ricId:" + ricId + " ,rpcTimeOut:" + rpcTimeOut);
        if (lock != null) {
          log.info("rpcId:" + rpcId + " ,rpcTimeOut:" + rpcTimeOut);
          // 第三位参数为标记加锁直到线程运行结束,不为空则加lock无限时锁
//          if (StringUtils.isNotBlank(isLockThreadEnd)) {
//            isLock = lock.tryLock();
//          } else {
            //            isLock=lock.tryLock(0l, 80000, TimeUnit.MILLISECONDS);
            isLock = lock.tryLockAndStartWatchDog(0l, 90000, TimeUnit.MILLISECONDS);
//          }
        }

        log.info(rpcId + " 加锁结果:" + isLock + "  获得锁时间:" + System.currentTimeMillis());
        if (!isLock) {
          log.error("线程:" + Thread.currentThread().getId() + "redis锁:" + rpcId + "加锁失败");
          //          返回500加锁异常信息给消费端
          //          throw new RuntimeException(
          //                  ProviderRPCService.RPCID
          //                          + ":"
          //                          + rpcId
          //                          + " 被其他服务锁住还没执行完毕无法释放,"
          //                          + "本服务不能执行,在没锁的情况下"
          //                          + "如果本服务正常执行完不报错调用方的事务不会回滚,"
          //                          + "上个服务也正常执行完了没报错调用方的事务也不会回滚,"
          //                          + "那么该接口相当于重复执行俩次造成数据错误,非幂等"
          //                          + "服务不可执行重复异常!!!!!!!");
          ResultDto resultDto =
              ResultDto.createResult(500, null, "无法加锁", null, null, "消费端" + rpcId + "加锁失败");
          result = ResponseEntity.ok(resultDto);

        }
        //        if (isLock) {
        //          throw new RuntimeException("provider测试异常返回");
        //        }
        // 拿到注解的值
        //        MethodSignature signature = (MethodSignature) point.getSignature();
        //        Method method = signature.getMethod();
        //        RPCIdempotentLockTarget annotation =
        // method.getAnnotation(RPCIdempotentLockTarget.class);

        // 根据你的需求获取标注在注解的值
        //      String key = annotation.key();
        //      int lockTime = annotation.lockTime();
        //      log.info("切面介入工作....前置通知 获取lockTime:" + lockTime);

        //        if (args != null && args.length > 0) {
        //          // 根据你的需求获取被切方法的args参数
        //        }
        else {
          // 加锁成功执行业务代码
          result = invocation.proceed();
        }

      } catch (Exception e) {
        //      log.info("切面介入工作....异常通知");
        exception=e;
      } finally {

        // finally语句有一个执行细节，如果在try或者catch语句内有return语句，
        // 则return语句在finally语句执行结束后才执行，但finally并不能改变返回值
        //      log.info("切面介入工作....后置通知:本线程加锁成功则释放锁 lock.unlock");
        //        long requestStartTime =
        //            (long)
        //                RPCWrapper.getHttpServletRequestRPCWrapper()
        //                    .getAttribute(ProviderRequtestParam.REQUESTSTARTTIME);

        //        long nowTime = System.currentTimeMillis();
        // 执行时间
        //        long exeTime = nowTime - requestStartTime;
        //        boolean moderThenConsumerReadTimeout = false;
        // provider无关代码执行时间以及真正到provider时consumer已经在计算请求处理时间了
        // 必须未超过请求处理时间才能释放锁考虑的是feign调用超时了在重试途中这里开始解锁导致重试的可以获取到锁再次执行upd,接口不幂等的情况会导致数据错乱
        //        超时的不主动释放锁在下个feign重试的时候获取不到锁直接抛出异常结束本次远程调用这样导致串行化远程调用,性能过低
        //        long comsumerReadTimeOut = providerRPCServiceImpl.getConsumerReadTimeout();
        //        if (lock.isLocked() && exeTime + ProviderRPCService.MORERPCTIMEOUT <
        // comsumerReadTimeOut) {

        if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
          lock.unlock();
          log.info(rpcId + "锁释放成功");
        } else {
          if (!lock.isLocked()) {
            log.info(rpcId+" redis锁已过期失效");
          }
          if (!lock.isHeldByCurrentThread()) {
            log.error(rpcId+" redis锁非当前线程持有");
          }
        }
        //抛出异常前需要先释放锁防止redis看门狗定时任务无限续锁
        if(exception!=null){
          throw exception;
        }
      }
      //        log.info("xid方法执行后测试finally是否最后执行先进后出xidaop:");
      //      }
    } else {
      result = invocation.proceed();
    }
    return result;
  }

  //  public static void main(String[] args) throws ClassNotFoundException {
  ////   System.out.println(
  ////
  // ClassUtils.isPresent("java.lang.annotation.Annotation",RpcidAnnotationnStrategy.class.getClassLoader()));
  ////    System.out.println(
  // Class.forName("java.lang.annotation.Annotation").equals(java.lang.annotation.Annotation.class));
  //    Object annotationStrategy= new RpcidAnnotationnStrategy();
  //    System.out.println(annotationStrategy.getClass().getName());
  //
  //  }

}
